package com.lordjoe.collectiveintelligence.data;

import java.util.*;

/**
 * com.lordjoe.collectiveintelligence.data.MeasurementSetDistribution
 *
 * @author Steve Lewis
 * @date May 12, 2009
 */
public class MeasurementSetDistribution<T extends Comparable> implements IDistribution<T>
{
    public static MeasurementSetDistribution[] EMPTY_ARRAY = {};
    public static Class THIS_CLASS = MeasurementSetDistribution.class;

    private final IMeasurementType<T> m_Type;
    private final IMeasurementSet<T> m_Data;
    private T m_Median;
    private double m_Mean;
    private double m_StandardDeviation;
    private IPair<T> m_Extrama;

    public MeasurementSetDistribution(IMeasurementType<T> pType, IMeasurementSet<T> pData)
    {
        m_Type = pType;
        m_Data = pData;
        buildDistribution();
    }

    public double normalize(T t)
    {
        double value = 0;
        if(t instanceof Number)
            value = ((Number)t).doubleValue();
        double ret = value - getMean();
        if(getStandardDeviation() > 0)
            ret /= getStandardDeviation();
        return ret;
    }

    protected void buildDistribution()
    {
        IMeasurementSet<T> data = getData();
        IMeasurement<T>[] iMeasurements = data.getItems();
        List<Comparable> holder = new ArrayList<Comparable>();

        for (int i = 0; i < iMeasurements.length; i++) {
            IMeasurement<T> m = iMeasurements[i];
            Comparable value = m.getValue();
            if (value != null)
                holder.add(value);
        }
        Comparable[] ret = new Comparable[holder.size()];
        holder.toArray(ret);
        Arrays.sort(ret);
        if (ret.length == 0)
            return;
        ;
        Object first = ret[0];
        Object last = ret[ret.length - 1];
        m_Median = (T) ret[ret.length / 2];
        m_Extrama = new ComparablePair((T) first, (T) last);
        if(first instanceof Number) {
            double sum = 0;
            double sumsq = 0;
            int number = ret.length;
            for (int i = 0; i < number; i++) {
                double n = ((Number)ret[i]).doubleValue();
                sum += n;
                sumsq += n * n;
            }
            m_Mean = sum / ret.length;
            m_StandardDeviation = Math.sqrt(sumsq /number - m_Mean * m_Mean );
            return;
        }
        if(first instanceof Boolean) {
             double sum = 0;
             double sumsq = 0;
             int number = ret.length;
             for (int i = 0; i < number; i++) {
                 double n = ((Boolean)ret[i]).booleanValue() ? 1 : 0;
                 sum += n;
                 sumsq += n * n;
             }
             m_Mean = sum / ret.length;
             m_StandardDeviation = Math.sqrt(sumsq /number - m_Mean * m_Mean );
             return;
         }
    }

    public IMeasurementType<T> getType()
    {
        return m_Type;
    }

    public IMeasurementSet<T> getData()
    {
        return m_Data;
    }

    public T getMedian()
    {
        return m_Median;
    }

    public double getMean()
    {
        return m_Mean;
    }

    public double getStandardDeviation()
    {
        return m_StandardDeviation;
    }

    public IPair<T> getExtrema()
    {
        return m_Extrama;
    }
}
